package com.intsig.airflow.web.dto;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.google.common.base.Converter;
import com.intsig.airflow.web.exception.AirflowWebDesignException;
import com.intsig.airflow.web.model.DagEdge;
import com.intsig.airflow.web.model.DagEntity;
import com.intsig.airflow.web.model.DagNode;
import com.intsig.airflow.web.util.ReflectUtil;
import com.intsig.airflow.web.valid.First;
import lombok.Data;

import javax.annotation.Nullable;
import javax.validation.GroupSequence;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author leojie 2021/3/13 7:52 下午
 */
@Data
@GroupSequence(value = {First.class, DagEntityDto.class})
public class DagEntityDto {
    /**
     * DAG 的名称
     */
    @NotBlank(message = "Dag标签不能为空", groups = {First.class})
    private String label;

    /**
     * 维护的节点、边的数据模型
     */
    private Map<String, List<Map<String, Object>>> value;

    public DagEntity convertTo() {
        DagEntityDtoConvert dagEntityDtoConvert = new DagEntityDtoConvert();
        return dagEntityDtoConvert.convert(this);
    }

    public DagEntityDto convertFor(DagEntity dagEntity) {
        DagEntityDtoConvert dagEntityDtoConvert = new DagEntityDtoConvert();
        return dagEntityDtoConvert.reverse().convert(dagEntity);
    }

    private static class DagEntityDtoConvert extends Converter<DagEntityDto, DagEntity> {

        @Override
        protected DagEntity doForward(@Nullable DagEntityDto dagEntityDto) {
            DagEntity dagEntity = new DagEntity();
            if (dagEntityDto == null) {
                return dagEntity;
            }
            dagEntity.setLabel(dagEntityDto.getLabel());

            final List<Map<String, Object>> edgeMapList = dagEntityDto.getValue().get(DagEntity.EDGES);
            List<DagEdge> edges = new ArrayList<>();
            for (Map<String, Object> edgeMap : edgeMapList) {
                DagEdge edge = new DagEdge();
                edge.setId(Long.parseLong(edgeMap.get(DagEdge.ID).toString()));
                edge.setSrcNodeId(Long.parseLong(edgeMap.get(DagEdge.SRC_NODE_ID).toString()));
                edge.setSrcOutputIdx(Integer.parseInt(edgeMap.get(DagEdge.SRC_OUTPUT_IDX).toString()));
                edge.setDstNodeId(Long.parseLong(edgeMap.get(DagEdge.DST_NODE_ID).toString()));
                edge.setDstInputIdx(Integer.parseInt(edgeMap.get(DagEdge.DST_INPUT_IDX).toString()));
                edges.add(edge);
            }
            dagEntity.addEdges(edges);

            final List<Map<String, Object>> nodeMapList = dagEntityDto.getValue().get(DagEntity.NODES);
            List<DagNode> nodes = new ArrayList<>();
            for (Map<String, Object> nodeMap : nodeMapList) {
                DagNode node = new DagNode();
                node.setId(Long.parseLong(nodeMap.get(DagNode.ID).toString()));
                final Object nodeName = nodeMap.get(DagNode.NAME);
                if(nodeName == null || StrUtil.isBlank(nodeName.toString())){
                    throw new AirflowWebDesignException("任务名称不能为空");
                }
                node.setName(nodeName.toString());
                node.setInPorts(ReflectUtil.changeObjToIntArr(nodeMap.get(DagNode.IN_PORTS)));
                node.setOutPorts(ReflectUtil.changeObjToIntArr(nodeMap.get(DagNode.OUT_PORTS)));
                node.setWidth(Integer.parseInt(nodeMap.getOrDefault(DagNode.WIDTH, "180").toString()));
                node.setHeight(Integer.parseInt(nodeMap.getOrDefault(DagNode.HEIGHT, "30").toString()));
                node.setPosX(Float.parseFloat(nodeMap.get(DagNode.POS_X).toString()));
                node.setPosY(Float.parseFloat(nodeMap.get(DagNode.POS_Y).toString()));
                node.setType(nodeMap.getOrDefault(DagNode.TYPE, DagNode.DEFAULT_TYPE_VALUE).toString());

                final Object eventObj = nodeMap.get(DagNode.RIGHT_CLICK_EVENT);
                if (eventObj != null) {
                    final JSONArray eventJsonArr = JSONUtil.parseArray(eventObj);
                    eventJsonArr.forEach(eventJsonObj -> {
                        JSONObject eventJson = JSONUtil.parseObj(eventJsonObj);
                        node.addNodeRightClickEvent(eventJson.getStr(DagNode.RIGHT_CLICK_EVENT_LABEL, ""),
                                eventJson.getStr(DagNode.RIGHT_CLICK_EVENT_NAME, ""));
                    });
                }

                Object nodeStyleObj = nodeMap.get(DagNode.NODE_STYLE);
                if (nodeStyleObj != null) {
                    final JSONObject nodeStyleObjJson = JSONUtil.parseObj(nodeStyleObj);
                    nodeStyleObjJson.keySet().forEach(styleName -> {
                        node.setNodeStyle(styleName, nodeStyleObjJson.getStr(styleName, ""));
                    });
                }
                nodes.add(node);
            }
            dagEntity.addNodes(nodes);
            return dagEntity;
        }

        @Override
        protected DagEntityDto doBackward(@Nullable DagEntity dagEntity) {
            DagEntityDto dagEntityDto = new DagEntityDto();
            if (dagEntity == null) {
                return dagEntityDto;
            }
            dagEntityDto.setLabel(dagEntity.getLabel());
            Map<String, List<Map<String, Object>>> value = new HashMap<>(2);

            List<Map<String, Object>> edges = new ArrayList<>(4);
            dagEntity.getDagEdges().forEach(dagEdge -> {
                Map<String, Object> edgeMap = new HashMap<>(5);
                edgeMap.put(DagEdge.ID, dagEdge.getId());
                edgeMap.put(DagEdge.SRC_NODE_ID, dagEdge.getSrcNodeId());
                edgeMap.put(DagEdge.SRC_OUTPUT_IDX, dagEdge.getSrcOutputIdx());
                edgeMap.put(DagEdge.DST_NODE_ID, dagEdge.getDstNodeId());
                edgeMap.put(DagEdge.DST_INPUT_IDX, dagEdge.getDstInputIdx());
                edges.add(edgeMap);
            });
            value.put(DagEntity.EDGES, edges);

            List<Map<String, Object>> nodes = new ArrayList<>(4);
            dagEntity.getDagNodes().forEach(dagNode -> {
                Map<String, Object> nodeMap = new HashMap<>(7);
                nodeMap.put(DagNode.ID, dagNode.getId());
                nodeMap.put(DagNode.NAME, dagNode.getName());
                nodeMap.put(DagNode.IN_PORTS, dagNode.getInPorts());
                nodeMap.put(DagNode.OUT_PORTS, dagNode.getOutPorts());
                nodeMap.put(DagNode.TYPE, dagNode.getType());
                nodeMap.put(DagNode.WIDTH, dagNode.getWidth());
                nodeMap.put(DagNode.HEIGHT, dagNode.getHeight());
                nodeMap.put(DagNode.POS_X, dagNode.getPosX());
                nodeMap.put(DagNode.POS_Y, dagNode.getPosY());
                nodeMap.put(DagNode.RIGHT_CLICK_EVENT, dagNode.getRightClickEvent());
                nodeMap.put(DagNode.NODE_STYLE, dagNode.getNodeStyle());
                nodes.add(nodeMap);
            });
            value.put(DagEntity.NODES, nodes);

            dagEntityDto.setValue(value);

            return dagEntityDto;
        }
    }

}
